package org.jxy.study.rpc.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import org.jxy.study.rpc.annotation.RpcService;
import org.jxy.study.rpc.message.Request;
import org.jxy.study.rpc.netty.hander.ByteMsgToDataMsgDecoder;
import org.jxy.study.rpc.netty.hander.DataMsgToByteMsgEncoder;
import org.jxy.study.rpc.netty.hander.DataMsgToRequestOrResponseConvert;
import org.jxy.study.rpc.netty.hander.NettyServerRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author : porteryao
 * @description : 描述
 * @date : 2021/3/11 11:30
 */
@Component
public class NettyServer implements ApplicationContextAware, InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private EventLoopGroup work = new NioEventLoopGroup(5);
    private EventLoopGroup boss = new NioEventLoopGroup();
    private Map<String, Object> serviceMap = new HashMap<>();
    @Value("${rpc.netty.server.address}")
    private String address;
    @Value("${rpc.netty.server.port}")
    private int port;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取服务的bean
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(RpcService.class);
        for(Object serviceBean:beans.values()){
            Class<?> clazz = serviceBean.getClass();
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> inter : interfaces){
                String interfaceName = inter.getName();
                logger.info("加载服务类: {}", interfaceName);
                serviceMap.put(interfaceName, serviceBean);
            }
        }
        logger.info("已加载全部服务接口:{}", serviceMap);
    }

    @Override
    public void afterPropertiesSet()  {
        this.start();
    }

    public void start() {
        NettyServerRequestHandler serverRequestHandler=new NettyServerRequestHandler(serviceMap);
        new Thread(() -> {
            try {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(work, boss)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new ByteMsgToDataMsgDecoder());
                            pipeline.addLast(new DataMsgToByteMsgEncoder());
                            pipeline.addLast(new DataMsgToRequestOrResponseConvert(Request.class));
                            pipeline.addLast(serverRequestHandler);
                        }
                    });
                ChannelFuture channelFuture = bootstrap
                    .bind(new InetSocketAddress(address, port))
                    .sync();
                if (channelFuture.isSuccess()) {
                    logger.info("netty服务端已启动,IP:{},port:{}",address, port);
                }
            } catch (Exception e) {
                e.printStackTrace();
                boss.shutdownGracefully();
                work.shutdownGracefully();
            }
        }).start();
    }
}
